package interfaces.clients.connection;

import exceptions.*;
import interfaces.clients.authorization.AuthorizationSystem;
import interfaces.clients.authorization.Authorizer;
import interfaces.clients.dispatcher.Dispatcher;
import interfaces.clients.dispatcher.replies.BaseReply;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;

/**
 * A class implementing functionality of handling one client's queries.<br>
 * <br>
 * <b><u>For programmers: </u></b> when you write this class' run() method,
 * you can imagine that it's one-threaded application with only one diffirence -
 * when you access shared objects, you must synchronize your calls with other
 * Handlers
 *
 * @author Mihail A. Muhin
 */
public class Handler extends Thread {
	private Socket s = null;


	/**
	 * Creates a new handler.<br>
	 * <br>
	 *
	 * @param s socket to handle. MUST not be changed after by calling process.
	 */
	public Handler(Socket s) {
		this.s = s;
	}

	public void run() {
		try {
			internal_run();
		} catch (FatalException e) {
			Util.ShowFatalError(e.getMessage());
		} catch (FatalDebugException e) {
			Util.ShowFatalError(e.getMessage());
		} catch (CorrectableException e) {
			Util.LogError(e.getMessage());
		} catch (CorrectableDebugException e) {
			Util.LogError(e.getMessage());
		} catch (RuntimeException e) {
			Util.ShowFatalError(e.getMessage());
		}
	}

	private void internal_run() throws CorrectableException, CorrectableDebugException {
		/*
			TODO: set a timeout for about 10-20 sec
			for hackers who want to make server unavaliable
			by opening a socket and waiting
			TODO: set a limit for a number of connection made by one user
			for hackers who want to make server unavaliable
			by opening a great number of sockets
			*/
		ObjectInputStream ois = null;
		try {
			ois = new ObjectInputStream(s.getInputStream());
		} catch (IOException e) {
			throw new CorrectableException("Can't get socket's InputStream");
		}
		Request r = new Request();
		r.read(ois);

		int UID = 0;
		Authorizer a = AuthorizationSystem.getInstance();
		//UID = a.authorize(r.getAdata());
    UID = a.authorize(r.getAdata()).getId(); // changed by Manujlov Maxim

		BaseReply rep = Dispatcher.dispatchCommand(UID, r.getCdata());
		if (rep == null) {
			try {
				s.close();
			} catch (IOException e1) {
				throw new CorrectableDebugException("Couldn't close opened socket", e1);
			}
			throw new CorrectableException("Can't get socket's OutputStream");
		}

		ObjectOutputStream oos = null;
		try {
			oos = new ObjectOutputStream(s.getOutputStream());
			rep.write(oos);
			oos.flush();
		} catch (IOException e) {

		}

		try {
			s.close();
			s = null;
			oos = null;
			ois = null;
		} catch (IOException e1) {
			throw new CorrectableDebugException("Couldn't close opened socket", e1);
		}
	}
}
